/*
 * XDevice Monitoring Tool
 * Uses RPC interface to monitor Raft cluster status
 */

#include <xdevice/rpc.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>

#define DEFAULT_SOCKET_PATH "/tmp/xdevice.sock"

static void print_usage(const char* prog_name) {
    printf("Usage: %s [options] <command>\n", prog_name);
    printf("\nOptions:\n");
    printf("  -s <path>    Socket path (default: %s)\n", DEFAULT_SOCKET_PATH);
    printf("  -h           Show this help\n");
    printf("\nCommands:\n");
    printf("  status       Show node status\n");
    printf("  cluster      Show cluster information\n");
    printf("  log          Show Raft log information\n");
    printf("  devices      List devices\n");
    printf("  ping         Test connection\n");
    printf("  monitor      Continuously monitor (Ctrl+C to exit)\n");
}

static const char* state_to_string(int state) {
    switch (state) {
        case 0: return "Follower";
        case 1: return "Candidate";
        case 2: return "Leader";
        default: return "Unknown";
    }
}

static void print_node_status(const xdevice_rpc_node_status_t* status) {
    printf("Node Status:\n");
    printf("  Node ID: %d\n", status->node_id);
    printf("  State: %s (%d)\n", state_to_string(status->state), status->state);
    printf("  Current Term: %lu\n", status->current_term);
    printf("  Leader ID: %d\n", status->leader_id);
    printf("  Commit Index: %lu\n", status->commit_index);
    printf("  Last Applied: %lu\n", status->last_applied);
    printf("  Last Log Index: %lu\n", status->last_log_index);
    printf("  Last Log Term: %lu\n", status->last_log_term);
    printf("  Healthy: %s\n", status->is_healthy ? "Yes" : "No");
    printf("  Election Timeout: %lu ms\n", status->election_timeout_ms);
    printf("  Heartbeat Interval: %lu ms\n", status->heartbeat_interval_ms);
    
    time_t last_heartbeat = status->last_heartbeat_time / 1000;
    printf("  Last Heartbeat: %s", ctime(&last_heartbeat));
}

static void print_cluster_status(const xdevice_rpc_cluster_status_t* status) {
    printf("Cluster Status:\n");
    printf("  Total Nodes: %d\n", status->node_count);
    printf("  Active Nodes: %d\n", status->active_nodes);
    printf("  Leader ID: %d\n", status->leader_id);
    printf("  Cluster Term: %lu\n", status->cluster_term);
    printf("  Cluster Healthy: %s\n", status->cluster_healthy ? "Yes" : "No");
    
    printf("\nNode Details:\n");
    for (int i = 0; i < status->node_count && i < 16; i++) {
        const xdevice_rpc_node_status_t* node = &status->nodes[i];
        printf("  Node %d: %s, Term=%lu, Healthy=%s\n", 
               node->node_id, 
               state_to_string(node->state),
               node->current_term,
               node->is_healthy ? "Yes" : "No");
    }
}

static void print_log_info(const xdevice_rpc_log_info_t* log_info) {
    printf("Raft Log Information:\n");
    printf("  Log Range: %lu - %lu\n", log_info->start_index, log_info->end_index);
    printf("  Commit Index: %lu\n", log_info->commit_index);
    printf("  Entry Count: %u\n", log_info->entry_count);
    
    if (log_info->entry_count > 0) {
        printf("\nRecent Log Entries:\n");
        for (uint32_t i = 0; i < log_info->entry_count && i < 10; i++) {
            printf("  [%lu] Term=%lu, Type=%u, Size=%u, Data=\"%s\"\n",
                   log_info->entries[i].index, log_info->entries[i].term, 
                   log_info->entries[i].type, log_info->entries[i].data_size,
                   log_info->entries[i].data_preview);
        }
    }
}

static void print_device_list(const xdevice_rpc_device_list_t* device_list) {
    printf("Device List:\n");
    printf("  Total Devices: %d\n", device_list->device_count);
    
    if (device_list->device_count > 0) {
        printf("\nDevices:\n");
        for (int i = 0; i < device_list->device_count && i < 32; i++) {
            printf("  %s: Size=%lu bytes, State=%d, Type=%d, RO=%s\n",
                   device_list->devices[i].name, device_list->devices[i].size,
                   device_list->devices[i].state, device_list->devices[i].storage_type,
                   device_list->devices[i].readonly ? "Yes" : "No");
        }
    }
}

static int cmd_status(int client_fd) {
    xdevice_rpc_node_status_t status;
    
    if (xdevice_rpc_get_node_status(client_fd, &status) != XDEVICE_RPC_OK) {
        printf("Failed to get node status\n");
        return -1;
    }
    
    print_node_status(&status);
    return 0;
}

static int cmd_cluster(int client_fd) {
    xdevice_rpc_cluster_status_t status;
    
    if (xdevice_rpc_get_cluster_status(client_fd, &status) != XDEVICE_RPC_OK) {
        printf("Failed to get cluster status\n");
        return -1;
    }
    
    print_cluster_status(&status);
    return 0;
}

static int cmd_log(int client_fd) {
    xdevice_rpc_log_info_t log_info;
    
    if (xdevice_rpc_get_log_info(client_fd, &log_info) != XDEVICE_RPC_OK) {
        printf("Failed to get log information\n");
        return -1;
    }
    
    print_log_info(&log_info);
    return 0;
}

static int cmd_devices(int client_fd) {
    xdevice_rpc_device_list_t device_list;
    
    if (xdevice_rpc_get_device_list(client_fd, &device_list) != XDEVICE_RPC_OK) {
        printf("Failed to get device list\n");
        return -1;
    }
    
    print_device_list(&device_list);
    return 0;
}

static int cmd_ping(int client_fd) {
    if (xdevice_rpc_ping(client_fd) != XDEVICE_RPC_OK) {
        printf("Ping failed\n");
        return -1;
    }
    
    printf("Ping successful\n");
    return 0;
}

static int cmd_monitor(int client_fd) {
    printf("Monitoring XDevice cluster (Press Ctrl+C to exit)...\n\n");
    
    while (1) {
        system("clear");
        
        printf("=== XDevice Cluster Monitor ===\n");
        time_t now = time(NULL);
        printf("Time: %s\n", ctime(&now));
        
        // Show node status
        printf("--- Node Status ---\n");
        xdevice_rpc_node_status_t node_status;
        if (xdevice_rpc_get_node_status(client_fd, &node_status) == XDEVICE_RPC_OK) {
            printf("Node %d: %s (Term %lu, Commit %lu)\n",
                   node_status.node_id,
                   state_to_string(node_status.state),
                   node_status.current_term,
                   node_status.commit_index);
        } else {
            printf("Failed to get node status\n");
        }
        
        // Show cluster status
        printf("\n--- Cluster Status ---\n");
        xdevice_rpc_cluster_status_t cluster_status;
        if (xdevice_rpc_get_cluster_status(client_fd, &cluster_status) == XDEVICE_RPC_OK) {
            printf("Cluster: %d nodes, Leader=%d, Term=%lu, Healthy=%s\n",
                   cluster_status.node_count,
                   cluster_status.leader_id,
                   cluster_status.cluster_term,
                   cluster_status.cluster_healthy ? "Yes" : "No");
        } else {
            printf("Failed to get cluster status\n");
        }
        
        // Show devices
        printf("\n--- Devices ---\n");
        xdevice_rpc_device_list_t device_list;
        if (xdevice_rpc_get_device_list(client_fd, &device_list) == XDEVICE_RPC_OK) {
            printf("Device count: %d\n", device_list.device_count);
            for (int i = 0; i < device_list.device_count && i < 5; i++) {
                printf("  %s (%lu bytes)\n", 
                       device_list.devices[i].name,
                       device_list.devices[i].size);
            }
        } else {
            printf("Failed to get device list\n");
        }
        
        printf("\n(Refreshing in 2 seconds...)\n");
        sleep(2);
    }
    
    return 0;
}

int main(int argc, char* argv[]) {
    const char* socket_path = DEFAULT_SOCKET_PATH;
    const char* command = NULL;
    
    // Parse command line arguments
    int opt;
    while ((opt = getopt(argc, argv, "s:h")) != -1) {
        switch (opt) {
            case 's':
                socket_path = optarg;
                break;
            case 'h':
                print_usage(argv[0]);
                return 0;
            default:
                print_usage(argv[0]);
                return 1;
        }
    }
    
    if (optind >= argc) {
        printf("Error: Missing command\n");
        print_usage(argv[0]);
        return 1;
    }
    
    command = argv[optind];
    
    // Connect to RPC server
    int client_fd = xdevice_rpc_client_connect(socket_path);
    if (client_fd < 0) {
        printf("Failed to connect to XDevice RPC server at %s\n", socket_path);
        printf("Make sure XDevice is running with RPC enabled\n");
        return 1;
    }
    
    printf("Connected to XDevice RPC server\n");
    
    // Execute command
    int result = 0;
    if (strcmp(command, "status") == 0) {
        result = cmd_status(client_fd);
    } else if (strcmp(command, "cluster") == 0) {
        result = cmd_cluster(client_fd);
    } else if (strcmp(command, "log") == 0) {
        result = cmd_log(client_fd);
    } else if (strcmp(command, "devices") == 0) {
        result = cmd_devices(client_fd);
    } else if (strcmp(command, "ping") == 0) {
        result = cmd_ping(client_fd);
    } else if (strcmp(command, "monitor") == 0) {
        result = cmd_monitor(client_fd);
    } else {
        printf("Unknown command: %s\n", command);
        print_usage(argv[0]);
        result = 1;
    }
    
    // Cleanup
    xdevice_rpc_client_disconnect(client_fd);
    
    return result;
}
